/**
 * Copyright (C) 2009 - 2013 SC 4ViewSoft SRL
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.settings.audio.peq.peq.achart.chart;

import com.android.settings.audio.peq.peq.achart.model.CategorySeries;
import com.android.settings.audio.peq.peq.achart.renderer.DefaultRenderer;
import com.android.settings.audio.peq.peq.achart.renderer.DialRenderer;
import com.android.settings.audio.peq.peq.achart.util.MathHelper;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;

/**
 * The dial chart rendering class.
 */
public class DialChart extends RoundChart {
    /** The radius of the needle. */
    private static final int NEEDLE_RADIUS = 10;
    /** The series renderer. */
    private DialRenderer mRenderer;

    /**
     * Builds a new dial chart instance.
     *
     * @param dataset the series dataset
     * @param renderer the dial renderer
     */
    public DialChart(CategorySeries dataset, DialRenderer renderer) {
        super(dataset, renderer);
        mRenderer = renderer;
    }

    /**
     * The graphical representation of the dial chart.
     *
     * @param canvas the canvas to paint to
     * @param x the top left x value of the view to draw to
     * @param y the top left y value of the view to draw to
     * @param width the width of the view to draw to
     * @param height the height of the view to draw to
     * @param paint the paint
     */
    @Override
    public void draw(Canvas canvas, int x, int y, int width, int height, Paint paint) {
        paint.setAntiAlias(mRenderer.isAntialiasing());
        paint.setStyle(Style.FILL);
        paint.setTextSize(mRenderer.getLabelsTextSize());
        int legendSize = getLegendSize(mRenderer, height / 5, 0);
        int left = x;
        int top = y;
        int right = x + width;

        int sLength = mDataset.getItemCount();
        String[] titles = new String[sLength];
        for (int i = 0; i < sLength; i++) {
            titles[i] = mDataset.getCategory(i);
        }

        if (mRenderer.isFitLegend()) {
            legendSize = drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize,
                    paint, true);
        }
        int bottom = y + height - legendSize;
        drawBackground(mRenderer, canvas, x, y, width, height, paint, false, DefaultRenderer.NO_COLOR);

        int mRadius = Math.min(Math.abs(right - left), Math.abs(bottom - top));
        int radius = (int) (mRadius * 0.35 * mRenderer.getScale());
        if (mCenterX == NO_VALUE) {
            mCenterX = (left + right) / 2;
        }
        if (mCenterY == NO_VALUE) {
            mCenterY = (bottom + top) / 2;
        }
        float shortRadius = radius * 0.9f;
        float longRadius = radius * 1.1f;
        double min = mRenderer.getMinValue();
        double max = mRenderer.getMaxValue();
        double angleMin = mRenderer.getAngleMin();
        double angleMax = mRenderer.getAngleMax();
        if (!mRenderer.isMinValueSet() || !mRenderer.isMaxValueSet()) {
            int count = mRenderer.getSeriesRendererCount();
            for (int i = 0; i < count; i++) {
                double value = mDataset.getValue(i);
                if (!mRenderer.isMinValueSet()) {
                    min = Math.min(min, value);
                }
                if (!mRenderer.isMaxValueSet()) {
                    max = Math.max(max, value);
                }
            }
        }
        if (min == max) {
            min = min * 0.5;
            max = max * 1.5;
        }

        paint.setColor(mRenderer.getLabelsColor());
        double minorTicks = mRenderer.getMinorTicksSpacing();
        double majorTicks = mRenderer.getMajorTicksSpacing();
        if (minorTicks == MathHelper.NULL_VALUE) {
            minorTicks = (max - min) / 30;
        }
        if (majorTicks == MathHelper.NULL_VALUE) {
            majorTicks = (max - min) / 10;
        }
        drawTicks(canvas, min, max, angleMin, angleMax, mCenterX, mCenterY, longRadius, radius,
                minorTicks, paint, false);
        drawTicks(canvas, min, max, angleMin, angleMax, mCenterX, mCenterY, longRadius, shortRadius,
                majorTicks, paint, true);

        int count = mRenderer.getSeriesRendererCount();
        for (int i = 0; i < count; i++) {
            double angle = getAngleForValue(mDataset.getValue(i), angleMin, angleMax, min, max);
            paint.setColor(mRenderer.getSeriesRendererAt(i).getColor());
            boolean type = mRenderer.getVisualTypeForIndex(i) == DialRenderer.Type.ARROW;
            drawNeedle(canvas, angle, mCenterX, mCenterY, shortRadius, type, paint);
        }
        drawLegend(canvas, mRenderer, titles, left, right, y, width, height, legendSize, paint, false);
        drawTitle(canvas, x, y, width, paint);
    }

    /**
     * Returns the angle for a specific chart value.
     *
     * @param value the chart value
     * @param minAngle the minimum chart angle value
     * @param maxAngle the maximum chart angle value
     * @param min the minimum chart value
     * @param max the maximum chart value
     * @return the angle
     */
    private double getAngleForValue(double value, double minAngle, double maxAngle, double min,
                                    double max) {
        double angleDiff = maxAngle - minAngle;
        double diff = max - min;
        return Math.toRadians(minAngle + (value - min) * angleDiff / diff);
    }

    /**
     * Draws the chart tick lines.
     *
     * @param canvas the canvas
     * @param min the minimum chart value
     * @param max the maximum chart value
     * @param minAngle the minimum chart angle value
     * @param maxAngle the maximum chart angle value
     * @param centerX the center x value
     * @param centerY the center y value
     * @param longRadius the long radius
     * @param shortRadius the short radius
     * @param ticks the tick spacing
     * @param paint the paint settings
     * @param labels paint the labels
     * @return the angle
     */
    private void drawTicks(Canvas canvas, double min, double max, double minAngle, double maxAngle,
                           int centerX, int centerY, double longRadius, double shortRadius, double ticks, Paint paint,
                           boolean labels) {
        for (double i = min; i <= max; i += ticks) {
            double angle = getAngleForValue(i, minAngle, maxAngle, min, max);
            double sinValue = Math.sin(angle);
            double cosValue = Math.cos(angle);
            int x1 = Math.round(centerX + (float) (shortRadius * sinValue));
            int y1 = Math.round(centerY + (float) (shortRadius * cosValue));
            int x2 = Math.round(centerX + (float) (longRadius * sinValue));
            int y2 = Math.round(centerY + (float) (longRadius * cosValue));
            canvas.drawLine(x1, y1, x2, y2, paint);
            if (labels) {
                paint.setTextAlign(Align.LEFT);
                if (x1 <= x2) {
                    paint.setTextAlign(Align.RIGHT);
                }
                String text = i + "";
                if (Math.round(i) == (long) i) {
                    text = (long) i + "";
                }
                canvas.drawText(text, x1, y1, paint);
            }
        }
    }

    /**
     * Returns the angle for a specific chart value.
     *
     * @param canvas the canvas
     * @param angle the needle angle value
     * @param centerX the center x value
     * @param centerY the center y value
     * @param radius the radius
     * @param arrow if a needle or an arrow to be painted
     * @param paint the paint settings
     * @return the angle
     */
    private void drawNeedle(Canvas canvas, double angle, int centerX, int centerY, double radius,
                            boolean arrow, Paint paint) {
        double diff = Math.toRadians(90);
        int needleSinValue = (int) (NEEDLE_RADIUS * Math.sin(angle - diff));
        int needleCosValue = (int) (NEEDLE_RADIUS * Math.cos(angle - diff));
        int needleX = (int) (radius * Math.sin(angle));
        int needleY = (int) (radius * Math.cos(angle));
        int needleCenterX = centerX + needleX;
        int needleCenterY = centerY + needleY;
        float[] points;
        if (arrow) {
            int arrowBaseX = centerX + (int) (radius * 0.85 * Math.sin(angle));
            int arrowBaseY = centerY + (int) (radius * 0.85 * Math.cos(angle));
            points = new float[]{arrowBaseX - needleSinValue, arrowBaseY - needleCosValue,
                    needleCenterX, needleCenterY, arrowBaseX + needleSinValue, arrowBaseY + needleCosValue};
            float width = paint.getStrokeWidth();
            paint.setStrokeWidth(5);
            canvas.drawLine(centerX, centerY, needleCenterX, needleCenterY, paint);
            paint.setStrokeWidth(width);
        } else {
            points = new float[]{centerX - needleSinValue, centerY - needleCosValue, needleCenterX,
                    needleCenterY, centerX + needleSinValue, centerY + needleCosValue};
        }
        drawPath(canvas, points, paint, true);
    }

}
